'''
Created on May 10, 2011

@author: Zhao, Qi
'''

import os, re, logging, syslog, traceback, threading
from common import config, server_mail, framework
from datetime import datetime

LOG_LEVELS = {'DEBUG':  logging.DEBUG,
              'INFO':   logging.INFO,
              'WARNING':logging.WARNING,
              'ERROR':  logging.ERROR,
              'NOLOG':  100}

SYS_LOG_LEVEL = LOG_LEVELS.get(config.SYS_LOG_LEVEL)

LOG_DATE = None

# Added to fix bug of printing logs to emma log files of previous days by Bing on Jan 11, 2012
EMMA_LOG_HANDLER = None
EMMA_LOG_LOCK = threading.Lock()

def __get_log_date():
    return LOG_DATE

#single log file for each day
def __new_log_date():
    #'2011-11-07 14:24:44.004000'
    str_now = str(datetime.now())
    
    #['2011', '11', '07', '14', '43', '12', '812000']
    list_now = re.split(r'[^0-9]+', str_now)
    
    #unique log identity every day
    #'2011-11-07'
    return '-'.join(list_now[:3])
    
def __set_log_date(new_value):
    global LOG_DATE
    LOG_DATE = new_value

## logger creator
# Modified to fix bug of printing logs to emma log files of previous days by Bing on Jan 11, 2012
def initlog(log_name = None):
    global EMMA_LOG_HANDLER, EMMA_LOG_LOCK
    
    if log_name != None: # emma slow log
        log_file = os.path.join(config.LOG_ROOT, log_name)
        logger = logging.getLogger(log_name)
        hdlr = logging.FileHandler(log_file)
        formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
        hdlr.setFormatter(formatter)
        logger.addHandler(hdlr)
        logger.setLevel(LOG_LEVELS.get(config.LOG_LEVEL))
        return logger
    
    else: # emma log
        #handler = logging.handlers.RotatingFileHandler(LOG_NAME, maxBytes=200000, backupCount=50)
        # add a daily logger, so there will be a log for each day
        #date_handler = logging.handlers.TimedRotatingFileHandler(LOG_NAME, when='D')
        
        EMMA_LOG_LOCK.acquire()
        try:
            log_date = __new_log_date()
            if log_date != __get_log_date():
                __set_log_date(log_date)
                log_file = os.path.join(config.LOG_ROOT, 'mm-%s.log' % log_date)
                logger = logging.getLogger('mm')
                if EMMA_LOG_HANDLER != None:
                    EMMA_LOG_HANDLER.flush()
                    EMMA_LOG_HANDLER.close()
                    logger.removeHandler(EMMA_LOG_HANDLER)
                
                hdlr = logging.FileHandler(log_file)
                formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
                hdlr.setFormatter(formatter)
                logger.addHandler(hdlr)
                EMMA_LOG_HANDLER = hdlr
                logger.setLevel(LOG_LEVELS.get(config.LOG_LEVEL))
        finally:
            EMMA_LOG_LOCK.release()
            return logger


log = initlog()
slow = initlog('mm_slow.log')
# Added device abbre. by Bing on Oct 27, 2011
# (Note: if we change the abbre. here, Daily Reporting program may need to be changed accordingly.)
device_abbr = {'iPhone' : 'iPhn',
               'iPad' : 'iPad',
               'Android' : 'Andr',
               'iPhone Simulator' : 'iPhS',
               'iPod touch' : 'iPoT'} 

def __log_check():
    global log
    #if it's time to create a new log file (new log identity)
    if __get_log_date() != __new_log_date():
        log = initlog()
        
def __log_prefix():
    try:
        head_param = framework.get_server_key() 
        # Modified to add device info by Bing on Oct 14, 2011 --Begin
        user = head_param[:head_param.index('|')]
        if user == None:
            user = 'UNKNOWN'
        dev_type = framework.get_device_model()
        if dev_type == None:
            dev_type = 'UNKNOWN'
        # Use device abbr. by Bing on Oct 27, 2011 
        elif dev_type in device_abbr:
            dev_type = device_abbr[dev_type] 
            
        return '[USER %s, DEVICE %s] ' % (user, dev_type)
        # Modified by Bing --End
    except Exception,e:
        return '[UNKNOWN] '
    
## we have 4 log level: ERROR, WARNING, INFO, DEBUG
## INFO, DEBUG levels:
##     allow 2 types of parameters: string, list/tuple
##     rows in list/tuple will be printed in multiple lines
## ERROR, WARNING levels:
##     allow 3 types of parameters: string, list/tuple, None
##     rows in list/tuple will be printed in multiple lines
##     no parameter or None, will log system traceback exception
## if you pass other types of parameters, it will be converted into string 
##
## usage samples:
## import emma_log
## emma_log.debug("some variable values")
## emma_log.info("some server info")
## emma_log.warning()  #some exception but won't affect the whole work flow
## emma_log.error()    #some critical exception

def error(msg = None, mail = False):
    try:
        __log_check()
        prefix = __log_prefix()
        
        if msg is None:
            for err in traceback.format_exc().split('\n'):
                log.error(prefix + err)
                if SYS_LOG_LEVEL <= LOG_LEVELS['ERROR']:
                    syslog.syslog(syslog.LOG_ERR, prefix + err)
        else:
            if type(msg) in (str,unicode):
                log.error(prefix + msg)
                if SYS_LOG_LEVEL <= LOG_LEVELS['ERROR']:
                    syslog.syslog(syslog.LOG_ERR, prefix + msg)
            elif type(msg) is tuple or type(msg) is list:
                for m in msg:
                    log.error(prefix + m)
                    if SYS_LOG_LEVEL <= LOG_LEVELS['ERROR']:
                        syslog.syslog(syslog.LOG_ERR, prefix + m)
            else:
                log.error(prefix + str(msg))
                if SYS_LOG_LEVEL <= LOG_LEVELS['ERROR']:
                    syslog.syslog(syslog.LOG_ERR, prefix + str(msg))
                
        if mail:
            server_mail.send_sys_mail(msg)
    except Exception:
        pass

def warning(msg = None, mail = False):
    try:
        __log_check()
        prefix = __log_prefix()
        
        if msg is None:
            for err in traceback.format_exc().split('\n'):
                log.warning(prefix + err)
                if SYS_LOG_LEVEL <= LOG_LEVELS['WARNING']:
                    syslog.syslog(syslog.LOG_WARNING, prefix + err)
        else:
            if type(msg) in (str,unicode):
                log.warning(prefix + msg)
                if SYS_LOG_LEVEL <= LOG_LEVELS['WARNING']:
                    syslog.syslog(syslog.LOG_WARNING, prefix + msg)
            elif type(msg) is tuple or type(msg) is list:
                for m in msg:
                    log.warning(prefix + m)
                    if SYS_LOG_LEVEL <= LOG_LEVELS['WARNING']:
                        syslog.syslog(syslog.LOG_WARNING, prefix + m)
            else:
                log.warning(prefix + str(msg))
                if SYS_LOG_LEVEL <= LOG_LEVELS['WARNING']:
                    syslog.syslog(syslog.LOG_WARNING, prefix + str(msg))

        if mail:
            server_mail.send_sys_mail(msg)
    except Exception:
        pass

def info(msg, mail = False):
    try:
        __log_check()
        prefix = __log_prefix()
        
        if type(msg) in (str,unicode):
            log.info(prefix + msg)
            if SYS_LOG_LEVEL <= LOG_LEVELS['INFO']:
                syslog.syslog(syslog.LOG_WARNING, prefix + msg)
        elif type(msg) is tuple or type(msg) is list:
            for m in msg:
                log.info(prefix + m)
                if SYS_LOG_LEVEL <= LOG_LEVELS['INFO']:
                    syslog.syslog(syslog.LOG_WARNING, prefix + m)
        else:
            log.info(prefix + str(msg))
            if SYS_LOG_LEVEL <= LOG_LEVELS['INFO']:
                syslog.syslog(syslog.LOG_WARNING, prefix + str(msg))

        if mail:
            server_mail.send_sys_mail(msg)
    except Exception:
        pass

def debug(msg, mail = False):
    try:
        __log_check()
        prefix = __log_prefix()

        if type(msg) in (str,unicode):
            log.debug(prefix + msg)
            if SYS_LOG_LEVEL <= LOG_LEVELS['DEBUG']:
                syslog.syslog(syslog.LOG_DEBUG, prefix + msg)            
        elif type(msg) is tuple or type(msg) is list:
            for m in msg:
                log.debug(prefix + m)
                if SYS_LOG_LEVEL <= LOG_LEVELS['DEBUG']:
                    syslog.syslog(syslog.LOG_DEBUG, prefix + m)                
        else:
            log.debug(prefix + str(msg))
            if SYS_LOG_LEVEL <= LOG_LEVELS['DEBUG']:
                syslog.syslog(syslog.LOG_DEBUG, prefix + str(msg))

        if mail:
            server_mail.send_sys_mail(msg)
    except Exception:
        pass

def tracking_log(msg = ''):
    try:
        msg = '[EMMA CLICK TRACK: %s] %s' % (framework.get_remote_addr(), msg)
        info(msg)
    except Exception:
        pass
